package com.katesoft.scale4j.rttp.mocks;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Ignore;

import com.hazelcast.config.Config;
import com.hazelcast.core.AtomicNumber;
import com.hazelcast.core.Cluster;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IList;
import com.hazelcast.core.ILock;
import com.hazelcast.core.IMap;
import com.hazelcast.core.IQueue;
import com.hazelcast.core.ISet;
import com.hazelcast.core.ITopic;
import com.hazelcast.core.IdGenerator;
import com.hazelcast.core.Instance;
import com.hazelcast.core.InstanceListener;
import com.hazelcast.core.LifecycleService;
import com.hazelcast.core.MultiMap;
import com.hazelcast.core.Semaphore;
import com.hazelcast.core.Transaction;
import com.hazelcast.logging.LoggingService;
import com.hazelcast.partition.PartitionService;
import com.katesoft.scale4j.rttp.client.ClusterIsNotActiveException;

/**
 * this class allows to facilitate hazelcast testing.
 * 
 * @author kate2007
 */
@Ignore
public class HazelcastInstanceStub implements HazelcastInstance {
   private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();

   private final String name = UUID.randomUUID().toString();
   private Cluster cluster;
   private boolean running = true;

   private final Map<String, IQueue<?>> queues = new HashMap<String, IQueue<?>>();
   private final Map<String, ITopic<?>> topics = new HashMap<String, ITopic<?>>();
   private final Map<String, ISet<?>> sets = new HashMap<String, ISet<?>>();
   private final Map<String, IList<?>> lists = new HashMap<String, IList<?>>();
   private final Map<String, IMap<?, ?>> maps = new HashMap<String, IMap<?, ?>>();
   private final Map<String, MultiMap<?, ?>> multiMaps = new HashMap<String, MultiMap<?, ?>>();
   private final Map<String, ILock> locks = new HashMap<String, ILock>();
   private final Map<String, IdGenerator> idGenerators = new HashMap<String, IdGenerator>();
   private final Map<String, AtomicNumber> atomicNumbers = new HashMap<String, AtomicNumber>();
   private final Map<String, Semaphore> semaphores = new HashMap<String, Semaphore>();

   @Override
   public String getName() {
      return name;
   }

   @SuppressWarnings("unchecked")
   @Override
   public synchronized <E> IQueue<E> getQueue(String name) {
      ensureRunning();
      IQueue<?> queue = queues.get(name);
      if (queue == null) {
         queue = mock(IQueue.class);
         queues.put(name, queue);
      }
      return (IQueue<E>) queue;
   }

   @SuppressWarnings("unchecked")
   @Override
   public synchronized <E> ITopic<E> getTopic(String name) {
      ensureRunning();
      ITopic<?> topic = topics.get(name);
      if (topic == null) {
         topic = mock(ITopic.class);
         topics.put(name, topic);
      }
      return (ITopic<E>) topic;
   }

   @SuppressWarnings("unchecked")
   @Override
   public synchronized <E> ISet<E> getSet(String name) {
      ensureRunning();
      ISet<?> set = sets.get(name);
      if (set == null) {
         set = mock(ISet.class);
         sets.put(name, set);
      }
      return (ISet<E>) set;
   }

   @SuppressWarnings("unchecked")
   @Override
   public synchronized <E> IList<E> getList(String name) {
      ensureRunning();
      IList<?> list = lists.get(name);
      if (list == null) {
         list = mock(IList.class);
         lists.put(name, list);
      }
      return (IList<E>) list;
   }

   @SuppressWarnings("unchecked")
   @Override
   public synchronized <K, V> IMap<K, V> getMap(String name) {
      ensureRunning();
      IMap<?, ?> map = maps.get(name);
      if (map == null) {
         map = mock(IMap.class);
         maps.put(name, map);
      }
      return (IMap<K, V>) map;
   }

   @SuppressWarnings("unchecked")
   @Override
   public synchronized <K, V> MultiMap<K, V> getMultiMap(String name) {
      ensureRunning();
      MultiMap<?, ?> multiMap = multiMaps.get(name);
      if (multiMap == null) {
         multiMap = mock(MultiMap.class);
         multiMaps.put(name, multiMap);
      }
      return (MultiMap<K, V>) multiMap;
   }

   @Override
   public synchronized ILock getLock(Object key) {
      ensureRunning();
      ILock lock = locks.get(key);
      if (lock == null) {
         lock = new HazelcastLockStub(key);
         locks.put(name, lock);
      }
      return lock;
   }

   @Override
   public synchronized Cluster getCluster() {
      if (cluster == null) {
         cluster = mock(Cluster.class);
         when(cluster.getClusterTime()).thenReturn(System.currentTimeMillis());
      }
      return cluster;
   }

   @Override
   public ExecutorService getExecutorService() {
      ensureRunning();
      return EXECUTOR_SERVICE;
   }

   @Override
   public ExecutorService getExecutorService(@SuppressWarnings("unused") String name) {
      ensureRunning();
      return EXECUTOR_SERVICE;
   }

   @Override
   public Transaction getTransaction() {
      return mock(Transaction.class);
   }

   @Override
   public synchronized IdGenerator getIdGenerator(String name) {
      ensureRunning();
      IdGenerator idGenerator = idGenerators.get(name);
      if (idGenerator == null) {
         idGenerator = mock(IdGenerator.class);
         idGenerators.put(name, idGenerator);
      }
      return idGenerator;
   }

   @Override
   public synchronized AtomicNumber getAtomicNumber(String name) {
      ensureRunning();
      AtomicNumber atomicNumber = atomicNumbers.get(name);
      if (atomicNumber == null) {
         atomicNumber = mock(AtomicNumber.class);
         atomicNumbers.put(name, atomicNumber);
      }
      return atomicNumber;
   }

   @Override
   public synchronized Semaphore getSemaphore(String name) {
      ensureRunning();
      Semaphore semaphore = semaphores.get(name);
      if (semaphore == null) {
         semaphore = mock(Semaphore.class);
         semaphores.put(name, semaphore);
      }
      return semaphore;
   }

   @Override
   public void shutdown() {
      running = false;
   }

   @Override
   public void restart() {
      running = true;
   }

   @Override
   public Collection<Instance> getInstances() {
      return new HashSet<Instance>() {
         private static final long serialVersionUID = 118257219251626679L;

         {
            addAll(queues.values());
            addAll(topics.values());
            addAll(sets.values());
            addAll(lists.values());
            addAll(maps.values());
            addAll(multiMaps.values());
            addAll(locks.values());
            addAll(idGenerators.values());
            addAll(atomicNumbers.values());
            addAll(semaphores.values());
         }
      };
   }

   @Override
   public void addInstanceListener(@SuppressWarnings("unused") InstanceListener instanceListener) {
      throw new UnsupportedOperationException();
   }

   @Override
   public void removeInstanceListener(@SuppressWarnings("unused") InstanceListener instanceListener) {
      throw new UnsupportedOperationException();
   }

   @Override
   public Config getConfig() {
      return mock(Config.class);
   }

   @Override
   public PartitionService getPartitionService() {
      return mock(PartitionService.class);
   }

   @Override
   public LoggingService getLoggingService() {
      return mock(LoggingService.class);
   }

   @Override
   public LifecycleService getLifecycleService() {
      LifecycleService lifecycleService = mock(LifecycleService.class);
      when(lifecycleService.isRunning()).thenReturn(running);
      return lifecycleService;
   }

   private void ensureRunning() {
      if (!running) {
         throw new ClusterIsNotActiveException(this);
      }
   }
}
